package com.mq.mqserver.core;

import com.mq.common.MqException;

public class Router {
    //路由规定:
    //routingKey: 只能由 数字 字母(大小写) 下划线 构成,使用.作为分割
    //bindingKey:只能包含 数字 字母 下划线 * #,以.作为分割,* #只能作为独立的分段
    //判断消息携带的绑定格式是否正确
    public boolean checkRoutingKey(String routingKey){
        char[] ch = routingKey.toCharArray();
        int n = ch.length;
        //当routingKey为空,就是交换机为fanout的情况,没有匹配规则,将连接交换机的队列全部发送消息
        if(n==0) return true;
        for(char i:ch){
            if(i>='a' && i<='z') {
                continue;
            }
            if(i>='A' && i<='Z') continue;
            if(i>='0' && i<='9') continue;
            if(i=='.' || i=='_') continue;
            //当存在字符上述情况都不满足时,就是非法字符,直接返回false
            return false;
        }
        return true;
    }

    //检查交换机和队列的绑定格式是否正确
    public boolean checkBindingKey(String bindingKey) {
        char[] ch = bindingKey.toCharArray();
        int n = ch.length;
        if(n==0) return true;
        for(char i:ch){
            if(i>='a' && i<='z') continue;
            if(i>='A' && i<='Z') continue;
            if(i>='0' && i<='9') continue;
            if(i=='.' || i=='_' || i=='*' || i=='#') continue;
            return false;
        }
        //判断* #是否符合规范
        //先将字符串按照.进行分割
        //以 .*.#.    .#.*.   .#.#.这3中字符串都规定为不符合规范的,这些在RabbitMQ中是符合的,
        //但他们的效果和.#.的效果相同,因此就不费力写代码是写这些功能了
        String[] s = bindingKey.split("\\.");
       int m = s.length;
       for(int i=0;i<m-1;i++){
           if(s[i].equals("*") && s[i+1].equals("#")) return false;
           if(s[i].equals("#") &&s[i+1].equals("*")) return false;
           if(s[i].equals("#") && s[i+1].equals("#")) return false;
       }
        return true;

    }
    //判断routingKey和bindingKey是否满足匹配,可以匹配成功
    public boolean route(ExchangeType type, Binding binding, String routing) throws MqException {
        if(type==ExchangeType.FANOUT){
            //可以匹配绑定该交换机的所有队列
            //无匹配要求,就是说用不上匹配规则
            return true;
        }else if(type==ExchangeType.TOPIC){
            return routeTopic(binding,routing);
        }else{
            throw new MqException("[ROuter] 交换机类型非法 ExchangeType:"+type);
        }
    }

    // [测试用例]
    // binding key          routing key         result
    // aaa                  aaa                 true
    // aaa.bbb              aaa.bbb             true
    // aaa.bbb              aaa.bbb.ccc         false
    // aaa.bbb              aaa.ccc             false
    // aaa.bbb.ccc          aaa.bbb.ccc         true
    // aaa.*                aaa.bbb             true
    // aaa.*.bbb            aaa.bbb.ccc         false
    // *.aaa.bbb            aaa.bbb             false
    // #                    aaa.bbb.ccc         true
    // aaa.#                aaa.bbb             true
    // aaa.#                aaa.bbb.ccc         true
    // aaa.#.ccc            aaa.ccc             true
    // aaa.#.ccc            aaa.bbb.ccc         true
    // aaa.#.ccc            aaa.aaa.bbb.ccc     true
    // #.ccc                ccc                 true
    // #.ccc                aaa.bbb.ccc         true
    private boolean routeTopic(Binding binding, String routing) {
        String[] bindings = binding.getBindingKey().split("\\.");
        String[] routings = routing.split("\\.");
        int b = 0;
        int r = 0;
        while(r<routings.length && b<bindings.length) {
            //binding为*,匹配routingKey一个分段
            if (bindings[b].equals("*")) {
                b++;
                r++;
                continue;
            }
            if (bindings[b].equals("#")) {
                b++;
                //为#,切值最后一个字符串
                if (b == bindings.length) return true;
                    //为#,向后匹配下一个分段
                else {
                    r = checkNextRouting(bindings[b], routings, r);
                    if (r==-1) {
                        //从routings当前位置向后找,找到了binding[b],就继续向后找,找不到直接返回false
                        return false;
                    }
                }
            } else {
//                    binding为普通字符
                if(!bindings[b].equals(routings[r])){
                    return  false;
                }
            }
            //当上述不满足的条件都不成立时,就进行下一组的判断
            b++;
            r++;
        }
        //当while条件不满足时,说明至少有一个字符串数组判断完了,但只有当两个字符串都匹配完才是匹配成功
        //不满足条件时,还是匹配失败的
        if(r!=routings.length || b!=bindings.length) {
            return false;
        }
        return true;
    }

    //当bindings当前为#,且不是最后一个分段是,要寻找binding的#下一个分段和routings当前位置之后的分段是否能匹配上
    //匹配上,返回routingKey匹配上位置的下标,匹配不上,返回-1,表示匹配失败
    private int checkNextRouting(String binding, String[] routings, int r) {
        for(int i=r;i<routings.length;i++){
            if(binding.equals(routings[i])){
                return i;
            }
        }
        return -1;
    }
}
